#include <iostream>
#include <string>
#include <utility>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <cmath>
#include <functional>
#include <mutex>
#include <chrono>
#include <thread>
#include <condition_variable>
#include <assert.h>
using namespace std;

typedef long long lint;


class H2Oasync {
    mutex charge_need, charge_have;
    condition_variable cv_need, cv_have;
    multiset<char> need, have;
public:
    int total;

    H2Oasync() {
        total = 0;
        need = {'h', 'h', 'o'};
    }

    void newWater(){
        //cerr<<" ";
        assert(need.empty());
        assert(have.size()==3);
        need = {'h', 'h', 'o'};
        cv_need.notify_all();
        have.clear();
    }

    void hydrogen(function<void()> releaseHydrogen) {
        {
            std::unique_lock<std::mutex> lck(charge_need);
            while (need.count('h') == 0) cv_need.wait(lck);
            need.erase(need.find('h'));
        }

        // releaseHydrogen() outputs "H". Do not change or remove this line.
        releaseHydrogen();

        {
            std::unique_lock<std::mutex> lck(charge_have);
            total += 1;
            have.insert('h');
            if (have.size()==3) newWater();
        }

        //this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    void oxygen(function<void()> releaseOxygen) {
        {
            std::unique_lock<std::mutex> lck(charge_need);
            while (need.count('o') == 0) cv_need.wait(lck);
            need.erase(need.find('o'));
        }

        // releaseOxygen() outputs "O". Do not change or remove this line.
        releaseOxygen();

        {
            std::unique_lock<std::mutex> lck(charge_have);
            total -= 2;
            have.insert('o');
            if (have.size()==3) newWater();
        }
        //this_thread::sleep_for(std::chrono::milliseconds(3000));
    }
};

class H2Osync {
    mutex charge;
    int total;
    condition_variable cv;
public:
    H2Osync() {
        total = 0;
    }

    void hydrogen(function<void()> releaseHydrogen) {
        std::unique_lock<std::mutex> lck(charge);
        while (total > 1) cv.wait(lck);

        // releaseHydrogen() outputs "H". Do not change or remove this line.
        releaseHydrogen();
        total += 1;
        cv.notify_all();
    }

    void oxygen(function<void()> releaseOxygen) {
        std::unique_lock<std::mutex> lck(charge);
        while (total < 0) cv.wait(lck);

        // releaseOxygen() outputs "O". Do not change or remove this line.
        releaseOxygen();
        total -= 2;
        cv.notify_all();
    }
};


H2Oasync ho;
void releaseOxygen()
{
    this_thread::sleep_for(std::chrono::milliseconds(1000));

    cerr << "O";
}
void releaseHydrogen()
{
    this_thread::sleep_for(std::chrono::milliseconds(1000));

    cerr << "H";
}
void gh()
{
    ho.hydrogen(releaseHydrogen);
}

void go()
{
    ho.oxygen(releaseOxygen);
}

int main()
{
    constexpr int n = 100;
    std::thread hthreads[2 * n];
    std::thread othreads[n];
    for (int i = 0; i < n; ++i)
    {
        hthreads[2*i] = std::thread(gh);
        hthreads[2 * i+1] = std::thread(gh);
        othreads[i] = std::thread(go);
    }
    cerr<<"all threads start\n";

    for (auto& th : othreads) th.join();
    for (auto& th : hthreads) th.join();

    return 0;
}
